package net.oschina.allchat.server.framework.core.internal.config;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import net.oschina.allchat.server.framework.core.config.DummyConfiguration;
import net.oschina.allchat.server.framework.core.config.IConfiguration;
import net.oschina.allchat.server.framework.core.config.IConfigurationManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LocalDirectoryConfigurationManager implements IConfigurationManager {
	private static final String SEPARATOR_DEPENDENCY_PATH = "_";
	private static final String POSTFIX_CONFIGURATION_FILE = ".ini";
	private static final String NAME_SERVICE_CONFIGURATION_FILE = "service.ini";
	private String servicesConfigDir;
	private Map<ConfigurationKey, IConfiguration> cache;
	private Logger logger = LoggerFactory.getLogger(LocalDirectoryConfigurationManager.class);
	
	public LocalDirectoryConfigurationManager(String configDir) {
		servicesConfigDir = configDir + "/services";
		cache = new HashMap<ConfigurationKey, IConfiguration>();
	}

	@Override
	public IConfiguration getConfiguration(String serviceId) {
		return getConfiguration(new ConfigurationKey(serviceId, null));
	}

	@Override
	public IConfiguration getConfiguration(String serviceId, String dependencyPath) {
		return getConfiguration(new ConfigurationKey(serviceId, dependencyPath));
	}
	
	private IConfiguration getConfiguration(ConfigurationKey key) {
		IConfiguration config = cache.get(key);
		if (config != null)
			return config;
		
		File serviceDir = new File(servicesConfigDir, key.serviceId);
		if (!serviceDir.exists() || !serviceDir.isDirectory()) {
			logger.warn(String.format("service configuration directory(%s) doesn't exist or isn't directory. " +
					"using dummy service configuration instead", serviceDir.getPath()));
			config = new ComponentDummyConfiguration(key);
		}
		
		if (config == null) {
			File fConfig = null;
			
			if (key.dependencyPath == null) {
				fConfig = new File(serviceDir, NAME_SERVICE_CONFIGURATION_FILE);
			} else {
				fConfig = new File(serviceDir, key.dependencyPath + POSTFIX_CONFIGURATION_FILE);
				
				if (!fConfig.exists() || !fConfig.isFile()) {
					int shortPathIndex = key.dependencyPath.lastIndexOf(SEPARATOR_DEPENDENCY_PATH);
					if (shortPathIndex != -1) {
						String componentShortId = key.dependencyPath.substring(shortPathIndex + 1) +
								POSTFIX_CONFIGURATION_FILE;
						fConfig = new File(serviceDir, componentShortId);
					}
				}
			}
			
			if (fConfig.exists() && fConfig.isFile()) {
				if (key.dependencyPath != null) {
					config = new ComponentLocalFileConfiguration(key, fConfig.getPath(), getConfiguration(key.serviceId));
				} else {
					config = new ComponentLocalFileConfiguration(key, fConfig.getPath());
				}
			}
		}
		
		if (config == null) {
			logger.warn(String.format("configuration[%s, %s] not found. instead of using a dummy configuration",
					key.serviceId, key.dependencyPath == null ? "null" : key.dependencyPath));
			config = new ComponentDummyConfiguration(key);
		}
		
		cache.put(key, config);
		
		return config;
	}
	
	private class ComponentDummyConfiguration extends DummyConfiguration {
		private ConfigurationKey configurationKey;
		
		public ComponentDummyConfiguration(ConfigurationKey configurationKey) {
			this.configurationKey = configurationKey;
		}

		@Override
		public String getString(String key) {
			return getString(key, null);
		}

		@Override
		public String getString(String key, String defaultValue) {
			if (IConfiguration.KEY_SERVICE_ID.equals(key)) {
				return configurationKey.serviceId;
			} else if (IConfiguration.KEY_COMPONENT_DEPENDENCY_PATH.equals(key)) {
				return configurationKey.dependencyPath;
			} else {
				return defaultValue;
			}
		}

		@Override
		public String[] keys() {
			if (configurationKey.dependencyPath == null) {
				return new String[] {
						IConfiguration.KEY_SERVICE_ID
					};
			} else {
				return new String[] {
						IConfiguration.KEY_SERVICE_ID,
						IConfiguration.KEY_COMPONENT_DEPENDENCY_PATH
					};				
			}

		}
		
	}
	
	class ConfigurationKey {
		public String serviceId;
		public String dependencyPath;
		
		public ConfigurationKey(String serviceId, String dependencyPath) {
			this.serviceId = serviceId;
			this.dependencyPath = dependencyPath;
		}
		
		@Override
		public int hashCode() {
			int hash = 7;
			
			hash += 31 * hash + serviceId.hashCode();
			hash += 31 * hash + (dependencyPath == null ? 0 : dependencyPath.hashCode());
			
			return hash;
		}
		
		@Override
		public boolean equals(Object obj) {
			ConfigurationKey other = (ConfigurationKey)obj;
			
			if (!other.serviceId.equals(this.serviceId)) {
				return false;
			}
			
			if (dependencyPath != null) {
				return dependencyPath.equals(other.dependencyPath);
			} else {
				return other.dependencyPath == null;
			}
		}
	}
}
